import sys
import os

# wxPython
# http://www.wxpython.org/
import wx

# PIL Library:
# http://www.pythonware.com/products/pil/
import Image
import ImageOps

# Windows specific
if sys.platform == 'win32':
    from ctypes import windll
    #from optparse import OptionParser

    # Python for Windows extensions
    # http://sourceforge.net/projects/pywin32/
    import win32con

verbose = False

#def main(imagefile=None, verbose=True):
def main(argv=None):
    if argv is None:
        argv = sys.argv
    imagepath = argv[1]
    
    app = wx.App(redirect=False)
    
    desk = GetOverall()
    #im = BasicFit(argv[1])
    
    im = Image.open(imagepath)
    
    #deskx, desky = desk.size
    #imx, imy = im.size
    
    #if imx/deskx > imy/desky:
    #	# shrink y
    #	wallpaper = im.resize((desky*imx/imy,desky))
    #else:
    #	# shrink x
    #	wallpaper = im.resize((deskx,deskx*imy/imx))

    # scale and crop image to fit
    wallpaper = ImageOps.fit(im, desk.size, Image.BICUBIC, centering=(0.5,)*2)
    
    wallshift = RollWallpaper(wallpaper, desk)
    
    target = os.path.split(os.path.abspath(sys.argv[1]))[0] + (r'\wallpaper.bmp')
    wallshift.save(target)
    #wallpaper.save(target) #show what it looks like without reverse tiling it
    
    if verbose:
        print "Image properties: ", wallpaper.format, wallpaper.size, wallpaper.mode
        
    SetWallpaper(target)
    
def GetOverall():
    nummon = wx.Display.GetCount()
    if verbose:
        print "Monitors found: %i" % (nummon+1)
    monitors = [wx.Display(i).GetGeometry() for i in range(nummon)]
    
    #TEST: inject values from a nontrivial setup
    #monitors = [wx.Rect(0, 0, 1920, 1080), wx.Rect(1920,   30, 1680, 1050)]		
    #monitors = [wx.Rect(0, 0, 1920, 1080), wx.Rect(1920, -390, 1050, 1680)]
    
    # determine overall coordinates
    overall = wx.Rect()
    for i, monitor in enumerate(monitors):
        if verbose:
            print "#%i" % i
            print " - size    : %i, %i" % tuple(monitor.GetSize())
            print " - location: %i, %i" % tuple(monitor.GetTopLeft())
        overall.Union(monitor)
    
    if verbose:
        print "Overall:"
        print " - size    : %i, %i" % tuple(overall.GetSize())
        print " - location: %i, %i" % tuple(overall.GetTopLeft())
    
    return overall
    
def RollWallpaper(source, deskrect):
    if verbose:
        print "Image properties: ", im.format, im.size, im.mode
        
    # roll image to adjust for negative coordinates (if applicable)
    if deskrect.y < 0 or deskrect.x < 0:
        # create canvas
        wallshift = Image.new(source.mode, source.size)
        
        # paste base image location
        wallshift.paste(source, tuple(deskrect.position))
        
        # generate coordinates to paste additional images to
        tileloc = tuple(sum(x) for x in zip(deskrect.position, source.size))
        
        if deskrect.y < 0 and deskrect.x < 0: 
            wallshift.paste(source, tileloc)
        if deskrect.y < 0: 
            wallshift.paste(source, (0, tileloc[1]))
        if deskrect.x < 0: 
            wallshift.paste(source, (tileloc[0], 0))
    else:
        wallshift = source
            
    return wallshift
    
def BasicFit(imagefile):
    wallpaper = Image.open(imagefile)
        
    if verbose:
        print "Image properties: ", wallpaper.format, wallpaper.size, wallpaper.mode
        
    return wallpaper

def SetWallpaper(bmppath):
    if sys.platform != 'win32':
        # Doesn't work on non-Windows platforms
        return -1
        
    """ Given a path to a bmp, set it as the wallpaper """
    result = windll.user32.SystemParametersInfoA(
            win32con.SPI_SETDESKWALLPAPER,
            0,
            bmppath,
            win32con.SPIF_UPDATEINIFILE | win32con.SPIF_SENDWININICHANGE
        )
    if not result:
        raise Exception("Unable to set wallpaper.")
    
    return 0
    
if __name__ == "__main__":
    sys.exit(main())